/*
 * File:   misc.h
 * Author: filipp
 *
 * Created on March 19, 2010, 6:15 PM
 */


#ifndef _POWDERGAME_H
#define _POWDERGAME_H


#include "IO_Manager.h"
#include <algorithm>

#define CELL_SIMPLE_METHOD

class GameManager;
/* particle descriptor contains iformation about the kind of particle
 * there will be only as many of these as there are kinds of particles, 
 * maybe 10 or 15. However, it becomes real easy to have new kinds of particles,
 * all wee need to do is add a new particle descriptor.
 * Right now, I'm just building a framework, so this thing wont' store too much
 * data. But later, it will have all the info about every kind of particle.
 * 
 * I'm seriously considering scrapping this in favor of c++ polymorphism. But
 * the reason I considered this approach is to save speed. Instead of having the
 * runtime waste time figuring out what kind of particle it is based on inheritance,
 * we could just have each particle store a pointer to the correct particle descriptor
 * (which is just one class, no polymorphism, that contains all the data. This
 * is simple, constant time, just access the memory kind of thing. But it could
 * limit the kinds of particles we can have.
 */
struct ParticleDescriptor{
	ParticleDescriptor(float n_density=1, bool cm=true, GameManager* gm) : density(n_density), canMove(cm), myManager(gm){}
	float density; //measured in grams per square centimeter, one pixel is one centimeter
	bool canMove;
	const GameManager* myManager;
};
class Particle{
public:
	Particle(ParticleDescriptor* type);
	void applyForce(const vec2f& force);
	void applyImpulse(const vec2f& impulse);
	void applyImpulseImmediate(const vec2f& impulse);
	void addVelocity(const vec2f& v);//for debugging
	void setVelocity(const vec2f& v);//for debugging
	void simulate(float seconds);//applies forces/impulses, calculates new velocity
	/* IMPORTANT: It is up to the GameManager class to
	 * move this particle based on velocity.
	 * misc.h has a function called pathTrace that takes in the velocity (scaled by time)
	 * and a vector reference, and fills that vector with all the coordinates traversed
	 * by this particle as it moves. It assumes that 0,0 is the particle's current cell,
	 * so just add them to vec2i(cell_x, cell_y).
	 */
	const vec2f& getVelocity() const; //access the velocity
	vec2f getMomentum() const;
private:
	ParticleDescriptor* descriptor;	//what kind of particle is this?
	vec2f velocity;					//measured in centimeters(pixels) per second
	vec2f forceAccumulator;			//measured in 1/(100*1000) newtons. a newton is kilogram*meter/second/second, this is gram*centimeter/second/second
	vec2f impulseAccumulator;		//same as above, but not time-dependent, measured in gram*centimeter/second
};
class Cell{
public:
	friend class GameManager;
	Cell(); //initialize everything to neutral (pressure would = 1)
	~Cell();
	void swapParticles(Cell& other); //assumes that either this or other has no particle, or particles pass through each other
	void interact();//TODO make this faster //this wind acts on the winds of the neighbours, but also applies a force to the particle in this cell
	void diffuse(float diff);//pressure is the same as fluid density (believe me, i took this in physics)
	bool simulate(float seconds); //effects of accumulators are applied to the wind, wind force applied to particle. GameManager must move particle (handle collisions)
	void applyForce(const vec2f& force);
	void applyImpulse(const vec2f& impulse);
	void forcePressure(float rate);
	void impulsePressure(float rate);
	void addWind(const vec2f& w);
	void setWind(const vec2f& w);
	void addPressure(float p);
	void setPressure(float p);
	vec2f thisPositionEndsUpHere(float seconds);
	void project(); //modifies pressure based on div, then modifies velocities based on relative pressures
private:
	void updateDiv(); //0 if neutral, positive if influx, negative if outpour, based on surrounding velocities
	float pressure; //measured in atmospheres. 1.0f is neutral pressure, 0 is vacum
	float div;
	vec2f wind; //force in gram*centimeter/second/second applied to object in this cell
	float p_pressure; //previous pressure
	vec2f p_wind; //previous wind
	vec2f windForeAccumulator; //change per second of wind
	vec2f windImpulseAccumulator; //change of wind, applied all at once, not dependent on time.
	float pressureForceAccumulator;
	float pressureImpulseAccumulator;
	//this is important
	Particle* particle;
	/* this points to the particle that is inside this cell, or NULL if the cell is empty
	 * When a particle moves from its cell to an empty cell, the starting cell's
	 * pointer gets set to NULL, and the destination cell's pointer gets set to
	 * the address of this particle. If a particle exists, it's pointer is stored
	 * in one of our cells. Once it is destroyed, the cell that contained it
	 * stores NULL in its particle pointer, and this particle gets deleted. All
	 * particles are on the heap.
	 * Don't worry about memory leaks, I'm reasonably confident I can get this
	 * to work smoothly without loosing any memory. Memory is used when a partcle
	 * is created. Memory is freed when a particle is destroyed. Wben a particle
	 * moves, we simply store it's pointer in a new cell, no memory is used or
	 * freed.
	 * On the other hand, Cells exist always. They are created when we initialize
	 * our game array. They are destroyed when the game is over, or when we need
	 * to resize the array (when the screen is resized).
	 */
	Cell** nArray; //calculating this one on the fly takes forever, cheaper to store for each cell.
};

class GameManager{
public:
	friend class GameContext; //this will be the interface between the game and the IO manager
	GameManager(IO_Manager* iomgr);
	~GameManager();
	void updateState();//simulate the game, time is determined based on when the last update was
	void updateScreen(std::stringstream& out) const;//this function will be called by GameContext when it receives a request from IO_Controller
	void processLclick(const vec2i& cartesianPosition); //spawns a bunch of default particles at position
	void addMagnitude(int i); //magnitude can be between 1 and 10
private:
//functions
	void resizeGame(vec2i newSize);//TODO works first time, breaks second time
	Cell& accessField(vec2i coords);
	const Cell& accessField(vec2i coords) const;
	void diffuseCells(float diff);
	void interactCells();
	void simulateCells(float seconds);
	void advectCells(float seconds);
	void projectCells();
	/* Field is accessed in a cartesian manner. (0,0) is the bottom left corner.
	 * The X axis increases to the right, and the Y axis increases going up.
	 */
	float interpPressure(vec2i coords, vec2f tpeuh);
	void updateNeighbours(); //update each cells' neighbours
	void cellNeighbours(vec2i cellCoords, Cell** nArray); //an array of pointers
	/* nArray points to an array of 8 cell pointers. Logically, index 0 is
	 * the cell to the right of the cell at cellCoords, 1 is the cell to the
	 * upper right, 2 is the cell directly above, and so on. It goes
	 * counterclockwise. If one of these doesn't exist (a cell that's on the
	 * left border wouldn't have an upper left, left, and lower left neighbours)
	 * it's set to NULL.
	 * Pretty picture:
	 *
	 *	  3	 2	1
	 *		***
	 *	  4	***	0
	 *		***
	 *	  5	 6	7
	 */
//members
	GameContext* myContext; //new GameContext on creation, delete on deletion
	IO_Manager* myManager;
	Cell* field; // on the heap, must be deleted when done
	vec2i fieldSize; //stores x and y sizes of field
	float magnitude; //radius of circle to spawn particles
	ParticleDescriptor defaultDescriptor;
//parameters passed to functions
	Cell* nArray[8];
	std::vector<vec2i> path;
	std::vector<vec2i> circle;
	std::vector<vec2i> ProcessRequests;
	/* ProcessRequests contains the coordinates of cells that contain particles
	 * that will move to a different cell during this simulation period. It is
	 * filled as each cell is processed, based on whether the particle will move
	 * outside the cell now. If it will, then the game manager will take care
	 * of the cllision detection and moving the particle from one cell to another.
	 */
	Uint32 lastStateUpdate;
	mutable float maxMag;
	mutable float maxVel;
};

class GameContext: public Context{
public:
	GameContext(GameManager* mgr);
	void updateState();
	void renderToScreen(std::stringstream& out);
	void resize(const vec2i& s);
	void processMouse(const MouseInfo& mouseinfo);
private:
	GameManager* myManager;
};


#endif /* _POWDERGAME_H */





